Repository navigation
Zendesk digest: link unflagged tickets, list new non-English ones; sogs-ban: show posted names - #76
Merged
Merged
Conversation
sogs-ban now lists every id before the confirmation prompt, and in a dry run, with the display name from its newest post in each room's last 256 messages: "posted as 'X'", "posted, with no display name" or "no recent posts". A room that cannot be read is named under the list. Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
A ticket the model did not mark worth looking into was only counted in the header's category tally, then recorded as reported, so it never reached Discord as a link at all. #28891, a lost recovery phrase, went that way. They now get one line under the abuse reports, grouped by category, most common first, with the same link budget and a count that covers them all. Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
The digest translated only the tickets it gave a card, a scope set for the Comment button those cards no longer carry. 2 of the 35 non-English tickets in the queue had a transcript. Every ticket the run classifies is now passed to attach_english. Unchanged tickets are skipped before classification, so each one is translated once per change in its conversation. Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
The digest wrote its English rendering into a ticket field that nothing reads any more. It now posts the private note `claude: english` writes, with the same marker, so whichever gets there first stops the other repeating it until the customer writes again. translate_conversation and english_note are shared by both. The field code, its env setting and its tests are gone. Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
… feat/digest-translate-every-ticket
…eat/digest-translate-every-ticket
This was referenced Oct 14, 2026
- Post the English notes after the digest and its state, so a slow translation cannot hold up or time out the post; skip abuse reports. - One 💤 line per category, each with its own link budget, and record only the tickets actually linked: one shared budget let a busy category hide a rare ticket and still mark it reported. - Guard each ticket's whole note step, so no error on one ticket reaches the digest. - A room that cannot be reached is listed as unread rather than aborting sogs-ban; SogsError moves to its own module, ending the ban/names cycle. - Drop references to the removed field code and document the 💤 lines. Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
The digest translated only weekday mornings, only tickets in its window, and every translation stretched a job that has a timeout and an alert. A second Zendesk trigger, on the customer writing, now calls the relay with "event": "customer", which runs `note_reply --english-if-needed`: the `claude: english` note when the conversation needs one, never a command, never on an app-store review, journal only otherwise, and covered by RELAY_DRY_RUN like the rest of the relay. An unknown event is a 400 rather than falling through to the commands. The digest is read-only again. post_english_notes and is_english go; recent_names no longer raises on a malformed room. Claude-Session: https://claude.ai/code/session_019cnvrsCXsqLykDyQburD2f
A long conversation was cut to 8000 characters from its end, dropping the customer's newest message mid-JSON, and the turns cut off counted as already English: a long English ticket got a transcript on every message, and a long foreign one a note whose marker covered a turn it never translated. Whole turns now go from the oldest end, the newest is clipped only when it alone is too long, and the note says how many were left out. The relay runs one note_reply at a time per ticket and waits 60 seconds after a customer message, restarted by each further one, so a burst of messages costs one translation and two runs cannot both post it. note_reply also re-reads the ticket for the marker just before writing.
…translating them The relay no longer translates when the customer writes: no customer event, no debounce, no --english-if-needed. The digest instead leads its entries with a 🌐 line linking every ticket reported for the first time that is not in English, with its language, for an agent to answer `claude: english` on. `claude: english` keeps the newest turns whole, drops the oldest when the conversation is too long and says how many, and checks for the transcript again just before writing it.
…ding a stale draft Once support has replied on a ticket, `claude: draft` gives Claude the conversation so far and the customer's newest message separately, and asks it to answer only the newest one without repeating earlier replies. A draft with a public comment after it is no longer amended, since it was already sent or overtaken. `claude: reply` no longer sends a draft the customer has written after. It says so and waits for `claude: reply [n] anyway`.
mpretty-cyro
approved these changes
Oct 15, 2026
- `anyway` only confirms on the command line itself, since the brief runs to the end of the note. - `claude: reply` also asks for confirmation when the draft was already sent. - The follow-up prompt treats earlier support replies as context, not as a fact source, and covers a follow-up the customer has not prompted. - A draft looks up the customer's side once rather than twice.
Only a reply this tool sent, or the customer writing, makes the next draft start fresh.
mpretty-cyro
approved these changes
Oct 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three changes in one PR so they can be iterated on together.
The digest links every ticket it reports
A ticket the model did not mark
worth_looking_intowas only counted in the header's category tally, then recorded as reported, so it never reached Discord as a link.They now get one line per category under the abuse reports, most common first:
Each category has its own link budget, so a busy category cannot push a rare ticket off. Within a category the oldest are linked first, so what overflows is the newest, which has the most runs left. A ticket past the budget is counted as
+N moreand not recorded as reported, so it stays eligible for the next run; so do highlights beyond the display cap.New non-English tickets are listed for
claude: englishThe digest used to write an English rendering into the "English transcript (do not edit)" field, for carded tickets only, to serve a Comment button that is gone; nothing reads the field now. That step,
ZENDESK_ENGLISH_FIELD_IDand the field code are removed, so the digest is read-only again.Instead, the first line under the header links every ticket reported for the first time whose classified language is not English, with that language:
An agent answers
claude: englishon each. A blank orunknownlanguage counts as English; a ticket shown again with 🔄 is not listed. The line records nothing as reported.claude: englishkeeps the newest turns whole when a conversation is too long, dropping the oldest, and says how many.sogs-banshows each account's posted nameBefore
Continue? [y/N], and in a dry run, each id is listed with the display name from its newest post in each room's last 256 messages (posted as 'X',posted, with no display name,no recent posts). SOGS stores no profiles, so the name only exists in posted messages. A room that cannot be read, reached or parsed is named under the list; it never stops the ban. Checked live against open.getsession.org.Tests
785 tests pass, ruff clean. New:
TestUnflaggedLineandTestNonEnglishLineintest_triage.py,FitTurnsintest_note_reply.py, andtests/sogs/test_names.py.